package com.tc.service.impl;

import com.tc.dto.LoginDTO;
import com.tc.domain.Sys_user;
import com.tc.service.IAuthService;
import com.tc.service.ISys_userService;
import com.tc.utils.CaptchaUtils;
import com.tc.utils.JWT;
import com.tc.utils.PasswordUtils;
import com.tc.utils.ResultVo;
import com.tc.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
// Redis支持 - 如需使用Redis请取消注释
// import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
// Redis支持 - 如需使用Redis请取消注释
// import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 认证服务实现类
 * </p>
 *
 * @author tc
 * @since 2025-07-10
 */
@Service
public class AuthServiceImpl implements IAuthService {

    @Autowired
    private ISys_userService userService;

    // Redis支持 - 如需使用Redis请取消注释并注释掉内存存储部分
    // @Autowired(required = false)
    // private StringRedisTemplate redisTemplate;

    // 内存存储方案 - 如使用Redis请注释掉以下部分
    // ==========================================================
    // 使用内存存储验证码和令牌黑名单 (生产环境建议使用Redis)
    private static final Map<String, String> captchaStore = new ConcurrentHashMap<>();
    private static final Map<String, Long> captchaExpireStore = new ConcurrentHashMap<>();
    private static final Map<String, Long> tokenBlacklistStore = new ConcurrentHashMap<>();
    private static final long CAPTCHA_EXPIRE_TIME = 300000; // 5分钟，单位：毫秒
    // ==========================================================

    // Redis配置常量 - 如需使用Redis请取消注释
    // private static final String CAPTCHA_PREFIX = "captcha:";
    // private static final String TOKEN_BLACKLIST_PREFIX = "token_blacklist:";
    // private static final long CAPTCHA_EXPIRE_TIME = 300; // 5分钟，单位：秒

    @Override
    public ResultVo<CaptchaVO> generateCaptcha() {
        String captchaKey = UUID.randomUUID().toString();
        String captchaCode = CaptchaUtils.generateCode();
        String captchaImage = CaptchaUtils.generateImage(captchaCode);

        // 内存存储方案 - 如使用Redis请注释掉以下部分
        // ==========================================================
        captchaStore.put(captchaKey, captchaCode);
        captchaExpireStore.put(captchaKey, System.currentTimeMillis() + CAPTCHA_EXPIRE_TIME);
        cleanExpiredCaptcha(); // 清理过期的验证码
        // ==========================================================

        // Redis存储方案 - 如需使用Redis请取消注释并注释掉内存存储部分
        // ==========================================================
        // if (redisTemplate != null) {
        //     redisTemplate.opsForValue().set(
        //         CAPTCHA_PREFIX + captchaKey, 
        //         captchaCode, 
        //         CAPTCHA_EXPIRE_TIME, 
        //         TimeUnit.SECONDS
        //     );
        // }
        // ==========================================================

        CaptchaVO captchaVO = new CaptchaVO();
        captchaVO.setCaptchaKey(captchaKey);
        captchaVO.setCaptchaImage(captchaImage);
        captchaVO.setExpiresIn(CAPTCHA_EXPIRE_TIME / 1000); // 内存存储版本
        // captchaVO.setExpiresIn(CAPTCHA_EXPIRE_TIME); // Redis版本

        return ResultVo.ok(captchaVO);
    }

    @Override
    public ResultVo<LoginVO> login(LoginDTO loginDTO) {
        // 验证验证码
        if (StringUtils.hasText(loginDTO.getCaptcha()) && StringUtils.hasText(loginDTO.getCaptchaKey())) {
            if (!validateCaptcha(loginDTO.getCaptchaKey(), loginDTO.getCaptcha())) {
                return ResultVo.error(400, "验证码错误或已过期");
            }
        }

        // 查询用户
        Sys_user user = userService.getByUsername(loginDTO.getUsername());
        if (user == null) {
            return ResultVo.error(400, "用户名不存在");
        }

        // 验证密码
        if (!PasswordUtils.matches(loginDTO.getPassword(), user.getPassword())) {
            return ResultVo.error(400, "用户名或密码错误");
        }

        // 检查用户状态
        if (!"0".equals(user.getStatus())) {
            return ResultVo.error(400, "用户已被禁用");
        }

        // 生成令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", user.getUserId());
        claims.put("username", user.getUsername());

        String accessToken = JWT.createToken(
            user.getUsername(), 
            user.getUserId().toString(), 
            claims
        );
        String refreshToken = JWT.createRefreshToken(
            user.getUsername(), 
            user.getUserId().toString()
        );

        // 获取用户信息和权限
        UserInfoVO userInfo = userService.getUserInfo(user.getUserId());
        List<MenuVO> menus = userService.getUserMenus(user.getUserId());

        // 更新最后登录时间
        userService.updateLastLoginTime(user.getUserId());

        // 构建返回结果
        LoginVO loginVO = new LoginVO();
        loginVO.setAccessToken(accessToken);
        loginVO.setRefreshToken(refreshToken);
        loginVO.setTokenType("Bearer");
        loginVO.setExpiresIn(JWT.getExpirationTime());
        loginVO.setUserInfo(userInfo);
        loginVO.setMenus(menus);

        return ResultVo.ok(loginVO);
    }

    @Override
    public ResultVo<Void> logout(String token) {
        if (!StringUtils.hasText(token)) {
            return ResultVo.error(400, "令牌不能为空");
        }

        // 验证令牌
        if (!JWT.validateToken(token)) {
            return ResultVo.error(400, "无效的令牌");
        }

        // 内存存储方案 - 如使用Redis请注释掉以下部分
        // ==========================================================
        long expireTime = System.currentTimeMillis() + (JWT.getExpirationTime() * 1000);
        tokenBlacklistStore.put(token, expireTime);
        cleanExpiredTokens(); // 清理过期的令牌
        // ==========================================================

        // Redis存储方案 - 如需使用Redis请取消注释并注释掉内存存储部分
        // ==========================================================
        // if (redisTemplate != null) {
        //     redisTemplate.opsForValue().set(
        //         TOKEN_BLACKLIST_PREFIX + token,
        //         "blacklist",
        //         JWT.getExpirationTime(),
        //         TimeUnit.SECONDS
        //     );
        // }
        // ==========================================================

        return ResultVo.ok();
    }

    @Override
    public ResultVo<LoginVO> refreshToken(String refreshToken) {
        if (!StringUtils.hasText(refreshToken)) {
            return ResultVo.error(400, "刷新令牌不能为空");
        }

        // 验证刷新令牌
        if (!JWT.validateToken(refreshToken)) {
            return ResultVo.error(400, "刷新令牌无效或已过期");
        }

        // 从令牌中获取用户信息
        String username = JWT.getUsernameFromToken(refreshToken);
        String userIdStr = JWT.getUserIdFromToken(refreshToken);

        if (!StringUtils.hasText(username) || !StringUtils.hasText(userIdStr)) {
            return ResultVo.error(400, "令牌信息不完整");
        }

        Long userId = Long.valueOf(userIdStr);

        // 查询用户
        Sys_user user = userService.getById(userId);
        if (user == null || !"0".equals(user.getStatus())) {
            return ResultVo.error(400, "用户不存在或已被禁用");
        }

        // 生成新的访问令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", user.getUserId());
        claims.put("username", user.getUsername());

        String newAccessToken = JWT.createToken(
            user.getUsername(),
            user.getUserId().toString(),
            claims
        );

        // 获取用户信息和权限
        UserInfoVO userInfo = userService.getUserInfo(user.getUserId());
        List<MenuVO> menus = userService.getUserMenus(user.getUserId());

        // 构建返回结果
        LoginVO loginVO = new LoginVO();
        loginVO.setAccessToken(newAccessToken);
        loginVO.setRefreshToken(refreshToken); // 刷新令牌保持不变
        loginVO.setTokenType("Bearer");
        loginVO.setExpiresIn(JWT.getExpirationTime());
        loginVO.setUserInfo(userInfo);
        loginVO.setMenus(menus);

        return ResultVo.ok(loginVO);
    }

    /**
     * 验证验证码
     */
    private boolean validateCaptcha(String captchaKey, String captchaCode) {
        // 内存存储方案 - 如使用Redis请注释掉以下部分
        // ==========================================================
        String storedCode = captchaStore.get(captchaKey);
        Long expireTime = captchaExpireStore.get(captchaKey);

        if (!StringUtils.hasText(storedCode) || expireTime == null) {
            return false;
        }

        // 检查是否过期
        if (System.currentTimeMillis() > expireTime) {
            captchaStore.remove(captchaKey);
            captchaExpireStore.remove(captchaKey);
            return false;
        }

        // 验证码验证成功后删除
        captchaStore.remove(captchaKey);
        captchaExpireStore.remove(captchaKey);

        return captchaCode.equalsIgnoreCase(storedCode);
        // ==========================================================

        // Redis存储方案 - 如需使用Redis请取消注释并注释掉内存存储部分
        // ==========================================================
        // if (redisTemplate == null) {
        //     // 如果没有Redis，暂时跳过验证码验证
        //     return true;
        // }
        //
        // String storedCode = redisTemplate.opsForValue().get(CAPTCHA_PREFIX + captchaKey);
        // if (!StringUtils.hasText(storedCode)) {
        //     return false;
        // }
        //
        // // 验证码验证成功后删除
        // redisTemplate.delete(CAPTCHA_PREFIX + captchaKey);
        //
        // return captchaCode.equalsIgnoreCase(storedCode);
        // ==========================================================
    }

    // 内存存储相关方法 - 如使用Redis请注释掉以下部分
    // ==========================================================
    /**
     * 清理过期的验证码
     */
    private void cleanExpiredCaptcha() {
        long currentTime = System.currentTimeMillis();
        captchaExpireStore.entrySet().removeIf(entry -> {
            if (currentTime > entry.getValue()) {
                captchaStore.remove(entry.getKey());
                return true;
            }
            return false;
        });
    }

    /**
     * 清理过期的令牌
     */
    private void cleanExpiredTokens() {
        long currentTime = System.currentTimeMillis();
        tokenBlacklistStore.entrySet().removeIf(entry -> currentTime > entry.getValue());
    }

    /**
     * 检查令牌是否在黑名单中
     */
    public boolean isTokenBlacklisted(String token) {
        Long expireTime = tokenBlacklistStore.get(token);
        if (expireTime == null) {
            return false;
        }
        
        // 如果过期了，从黑名单中移除
        if (System.currentTimeMillis() > expireTime) {
            tokenBlacklistStore.remove(token);
            return false;
        }
        
        return true;
    }
    // ==========================================================

    // Redis版本的令牌黑名单检查方法 - 如需使用Redis请取消注释并注释掉内存存储版本
    // ==========================================================
    // /**
    //  * 检查令牌是否在黑名单中 (Redis版本)
    //  */
    // public boolean isTokenBlacklisted(String token) {
    //     if (redisTemplate == null) {
    //         return false;
    //     }
    //     
    //     String blacklistValue = redisTemplate.opsForValue().get(TOKEN_BLACKLIST_PREFIX + token);
    //     return StringUtils.hasText(blacklistValue);
    // }
    // ==========================================================
}
